package unti

import (
	"answer5/adb"
	"answer5/log"
	"bytes"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"sort"
	"strings"
	"sync"
	"time"
)

var (
	curQuestion       *Question
	lock              sync.Mutex
	baiduRequest      *http.Request
	httpClient        = &http.Client{Timeout: time.Duration(3 * time.Second)}
	regSpace          = regexp.MustCompile(`\s`)
	regPunc           = regexp.MustCompile(`[\p{P}！【】；‘，。、￥……（）——：“《》？\~\|\$\^\+\=` + "`]")
	regCorrect        = regexp.MustCompile(`"correct":(\d)`)
	regStyle          = regexp.MustCompile(`"correct":(\d)`)
	baiduToken        string
	magiCookie        string
	questionImagePath = `D:\DownLoad\MEmu\answer\answer-xy.jpg`
	resultImagePath   = `answer-result.jpg`
)

func RandomOptions(options *[]*[]string) {
	//合并分组
	for _, option := range *options {
		sort.Sort(sort.StringSlice(*option))
	}
	optionMap := make(map[string]*[]string)
	for _, option := range *options {
		optionMap[strings.Join(*option, ",")] = option
	}

	if len(optionMap) > 1 {
		go log.LogPrint("个性题")
		go log.LogPrint(fmt.Sprint(optionMap))
		maxLen := 0
		curInd := 0
		allRightGroup := make([]*[]*[]string, len(optionMap))
		for _, curOption := range optionMap {
			allRight := make([]*[]string, 0)
			for i, _ := range *curOption {
				rights := make(map[string]bool)
				wrongs := make(map[string]bool)
				//假设i为正确的
				for _i, _item := range *curOption {
					if _i == i {
						rights[_item] = true
					} else {
						wrongs[_item] = true
					}
				}
				//log.Println("start:", rights, wrongs)

				isDone := false
				for _i := 0; _i < 10; _i++ {
					if matchXiguaOption(&rights, &wrongs, options) {
						isDone = true
						break
					}
				}
				if isDone {
					_rights := make([]string, 0)
					for _item := range rights {
						_rights = append(_rights, _item)
					}
					if len(_rights) >= maxLen {
						maxLen = len(_rights)
						allRight = append(allRight, &_rights)
					}
				}
			}
			allRightGroup[curInd] = &allRight
			curInd++
		}
		resultGroup := make([]*[]*[]string, 0)
		for _, group := range allRightGroup {
			result := make([]*[]string, 0)
			for _, res := range *group {
				if len(*res) == maxLen {
					result = append(result, res)
				}
			}
			resultGroup = append(resultGroup, &result)
		}

		minLen := 100
		for _, group := range resultGroup {
			if len(*group) < minLen {
				minLen = len(*group)
			}
		}

		resultMinGroup := make([]*[]*[]string, 0)
		for _, group := range resultGroup {
			if len(*group) == minLen {
				resultMinGroup = append(resultMinGroup, group)
			}
		}

		resultGroupMap := make(map[string]*[]*[]string)
		for _, group := range resultMinGroup {
			resultGroupMap[mergeGroup(group)] = group
		}

		result := make([]*[]*[]string, len(resultGroupMap))
		_i := 0
		for _, res := range resultGroupMap {
			result[_i] = res
		}
		go SendRandomOption(&result, &curQuestion.Round)
		for _, res := range result {
			for _, r := range *res {
				go log.LogPrint(strings.Join(*r, " | "))
			}
		}
	}
}
func mergeGroup(group *[]*[]string) string {
	arr := make([]string, 0)
	for _, g := range *group {
		arr = append(arr, *g...)
	}
	sort.Sort(sort.StringSlice(arr))
	return strings.Join(arr, "")
}
func matchXiguaOption(rights *map[string]bool, wrongs *map[string]bool, options *[]*[]string) bool {
	isDone := true
	for _, option := range *options {
		optionsLen := len(*option)
		for _, item := range *option {
			if (*rights)[item] == true || (*wrongs)[item] == true {
				optionsLen--
			}
		}
		if optionsLen > 1 {
			isDone = false
		} else if optionsLen == 1 {
			_i := 0
			_hasRight := false
			for i, item := range *option {
				if (*rights)[item] == true {
					_hasRight = true
				} else if (*wrongs)[item] == false {
					_i = i
				}
			}
			if _hasRight {
				(*wrongs)[(*option)[_i]] = true
			} else {
				(*rights)[(*option)[_i]] = true
			}
		}

		//log.Println("running: ",option,*rights,*wrongs)
	}
	return isDone
}

func NewQuestion(question *Question) {
	lock.Lock()
	defer lock.Unlock()
	go log.LogPrint("=============================================")
	go log.LogPrint(question.Title)
	go log.LogPrint(fmt.Sprint(question.Options))
	curQuestion = question
	go searchNewQuestion()
	go SendNewQuestion(curQuestion)
}
func searchNewQuestion() {
	var wg sync.WaitGroup
	opts := make([]string, len(curQuestion.Options))
	for _i, _o := range curQuestion.Options {
		opts[_i] = _o.Text
	}

	//百度
	baiduWordContent := &BaiduWordContent{}
	wg.Add(1)
	go baidu(&wg, &curQuestion.Title, baiduWordContent)

	//百度分词选项
	optionWord := make(map[string]bool)
	wg.Add(1)
	go baiduWordOption(&wg, &opts, &optionWord, 0)

	//百度分词题目
	titleWord := make(map[string]bool)
	wg.Add(1)
	go baiduWordTitle(&wg, &[]string{curQuestion.Title}, &titleWord, 0)

	wg.Wait()

	curQuestion.OptionWords = &optionWord

	//内容着色
	result, matches := highLight(baiduWordContent, &opts, &optionWord)

	sort.Sort(ContentSort(result))
	contents := make([]string, 0)
	for _, r := range result {
		if r.Content != "" {
			contents = append(contents, r.Content)
		}
	}

	go SendNewContent(&contents, &matches, &curQuestion.Round)
}
func baiduWordTitle(wg *sync.WaitGroup, options *[]string, word *map[string]bool, count int) {
	baiduWord(wg, options, word, count)
	go SendWordDataTitle(word, &curQuestion.Round)
}
func baiduWordOption(wg *sync.WaitGroup, options *[]string, word *map[string]bool, count int) {
	baiduWord(wg, options, word, count)
	go SendWordDataOptions(word, &curQuestion.Round)
}
func baidu(wg *sync.WaitGroup, title *string, wordContent *BaiduWordContent) {
	go log.LogPrint("开始百度")
	if wg != nil {
		defer wg.Done()
	}
	if baiduRequest == nil {
		baiduRequest, err = http.NewRequest("GET", "http://www.baidu.com/s", nil)
		if err != nil {
			SendErr(err, "baidu 异常0")
			return
		}
		baiduRequest.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0")
		baiduRequest.Header.Set("Accept-Encoding", "gzip, deflate, br")
		baiduRequest.Header.Set("Cookie", "BAIDUID=5F62D0E892B86A5DB7CFA47CE318212B:FG=1; BIDUPSID=5F62D0E892B86A5DB7CFA47CE318212B; PSTM=1447652198; BDUSS=k9Yb2FEdGQ2NU43R2hVa1g1T0psfndTazFycXc4c24yQmdTSUtLUkxqNW02cDVZSVFBQUFBJCQAAAAAAAAAAAEAAADoetkAd2tfbG92ZV9zeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZdd1hmXXdYb; BD_UPN=12314753; BDORZ=FFFB88E999055A3F8A630C64834BD6D0; H_PS_PSSID=1465_21105; H_PS_645EC=3d2fECU2g1U2Sa5bZru9muRLt75R12zynK67DTzIo%2FEVk%2F13z8qAGOQ0%2BtOI1TlLG1WLrQ")
	}

	q := baiduRequest.URL.Query()
	q["wd"] = []string{*title}
	q["ie"] = []string{"UTF-8"}
	baiduRequest.URL.RawQuery = q.Encode()

	resp, err := httpClient.Do(baiduRequest)
	if err != nil {
		SendErr(err, "baidu 异常1")
		return
	}
	defer resp.Body.Close()
	body, err := gzip.NewReader(resp.Body)
	if err != nil {
		SendErr(err, "baidu 异常2")
		return
	}

	// data, err := ioutil.ReadAll(body)
	// if err != nil {
	// 	fmt.Println("err->", err)
	// }
	// fmt.Println(string(data))

	html, err := goquery.NewDocumentFromReader(body)
	if err != nil {
		SendErr(err, "baidu 异常3")
		return
	}
	wordContent.Words = make(map[string]bool)
	html.Find("#content_left em").Each(func(i int, selection *goquery.Selection) {
		txt := regPunc.ReplaceAllString(selection.Text(), "")
		if len(txt) > 1 {
			wordContent.Words[txt] = true
		}
	})

	//contentNodes := html.Find("#content_left .c-abstract")
	contentNodes := html.Find("#content_left .c-container")
	wordContent.Contents = make([]string, contentNodes.Length())
	contentNodes.Each(func(i int, selection *goquery.Selection) {
		selection.Find("style,script").Remove()
		txt := regSpace.ReplaceAllString(selection.Text(), "")
		wordContent.Contents[i] = txt
	})
}
func baiduWord(wg *sync.WaitGroup, options *[]string, word *map[string]bool, count int) {
	wordString := strings.Join(*options, "。")
	go log.LogPrint("开始分词: " + wordString)
	defer wg.Done()

	resp, err := httpClient.Post("https://aip.baidubce.com/rpc/2.0/nlp/v1/lexer?charset=UTF-8&access_token="+baiduToken, "application/json", bytes.NewBuffer([]byte(`{"text":"`+wordString+`"}`)))

	if err != nil {
		SendErr(err, "baiduWord 异常1")
		return
	}
	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		SendErr(err, "baiduWord 异常2")
		return
	}

	var wordData BaiduWordResponse
	if err = json.Unmarshal(data, &wordData); err != nil {
		SendErr(err, "baiduWord 异常3")
		return
	}
	if wordData.ErrorCode > 0 {
		fmt.Println(string(data))
		go log.ErrorPrint(string(data))
		if count == 0 {
			getBaiduToken()
			wg.Add(1)
			baiduWord(wg, options, word, 1)
		}
	} else {
		for _, w := range wordData.Items {
			if w.Pos != "w" {
				(*word)[w.Item] = true
			}
		}
	}
}
func getBaiduToken() {
	go log.LogPrint("getBaiduToken")
	//resp, err := http.NewRequest("POST", "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=wdZ6TEEtNBSWG5isI9WXkwWp&client_secret=mzT1tmlBum7FX89PXGaC0Qxc5I47dE1C", nil)
	resp, err := httpClient.Post("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=wdZ6TEEtNBSWG5isI9WXkwWp&client_secret=mzT1tmlBum7FX89PXGaC0Qxc5I47dE1C", "", nil)
	if err != nil {
		SendErr(err, "getBaiduToken 异常1")
		return
	}

	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		SendErr(err, "getBaiduToken 异常2")
		return
	}

	var wordData BaiduTokenResponse
	if err = json.Unmarshal(data, &wordData); err != nil {
		SendErr(err, "getBaiduToken 异常3")
		return
	}

	baiduToken = wordData.Token

	go SendBaiduToken(wordData.Token)
	//log.Println(baiduToken)

}
func magi(wg *sync.WaitGroup, word string, wordContent *BaiduWordContent) {
	if wg != nil {
		defer wg.Done()
	}
	go log.LogPrint("开始magi: " + word)
	//baikeContent := &BaiduWordContent{}
	//fmt.Println(word)

	v := url.Values{}
	v.Add("q", word)
	vCode := v.Encode()

	//log.Println("oldCookie",magiCookie)
	req, err := http.NewRequest("GET", "https://magi.com/search", nil)
	req.Header.Add("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9")
	req.Header.Add("accept-encoding", "gzip, deflate, br")
	req.Header.Add("accept-language", "zh-CN,zh;q=0.9,zh-TW;q=0.8,en-US;q=0.7,en;q=0.6")
	req.Header.Add("cookie", magiCookie)
	req.Header.Add("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36")
	//v = url.Values{}
	//v.Add("q", word)
	//req.URL.RawQuery = v.Encode()
	req.URL.RawQuery = vCode
	//resp, err := httpClient.Get("https://magi.com/search?"+v.Encode())
	resp, err := httpClient.Do(req)
	if err != nil {
		SendErr(err, "magi 异常1")
		return
	}
	defer resp.Body.Close()
	body, err := gzip.NewReader(resp.Body)
	if err != nil {
		SendErr(err, "magi 异常3")
	}

	//data, _ := ioutil.ReadAll(body)
	//fmt.Println(string(data))

	html, err := goquery.NewDocumentFromReader(body)
	if err != nil {
		SendErr(err, "magi 异常2")
		return
	}
	//contentNodes := html.Find("#result .fact")
	contentNodes := html.Find(`#result .fact,div[data-type="web"]`)
	//log.Println(contentNodes.Length())

	wordContent.Contents = make([]string, contentNodes.Length())
	contentNodes.Each(func(i int, selection *goquery.Selection) {
		txt := regSpace.ReplaceAllString(selection.Text(), "")
		wordContent.Contents[i] = txt
	})
	//fmt.Println(html.Text())

	//contentNodes = html.Find(`.card`)
	//fmt.Println(contentNodes.Length())
	//wordContent.Contents=contents
}
func baike(round *int32, word *string) {
	go log.LogPrint("开始百科: " + *word)
	opts := make([]string, len(curQuestion.Options))
	for _i, _o := range curQuestion.Options {
		opts[_i] = _o.Text
	}

	req, err := http.NewRequest("GET", "https://baike.baidu.com/item/"+*word, nil)
	if err != nil {
		SendErr(err, "baike 异常1")
		return
	}
	resp, err := httpClient.Do(req)
	if err != nil {
		SendErr(err, "baike 异常2")
		return
	}
	defer resp.Body.Close()

	html, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		SendErr(err, "baike 异常3")
		return
	}
	contentNodes := html.Find(".para")
	baikeContent := BaiduWordContent{}
	baikeContent.Contents = make([]string, contentNodes.Length())
	contentNodes.Each(func(i int, selection *goquery.Selection) {
		txt := regSpace.ReplaceAllString(selection.Text(), "")
		baikeContent.Contents[i] = txt
	})
	result, matches := highLight(&baikeContent, &opts, curQuestion.OptionWords)
	sort.Sort(ContentSort(result))
	contents := make([]string, 0)
	for _, r := range result {
		if r.Content != "" {
			contents = append(contents, r.Content)
		}
	}

	if len(contents) == 0 {
		baiduWordContent := &BaiduWordContent{}
		baidu(nil, word, baiduWordContent)
		result, matches = highLight(baiduWordContent, &opts, curQuestion.OptionWords)
		sort.Sort(ContentSort(result))
		//fmt.Println(baiduWordContent)
		for _, r := range result {
			if r.Content != "" {
				contents = append(contents, r.Content)
			}
		}
		//if len(contents) == 0 {
		//	var wg sync.WaitGroup
		//	magiContents := make([]*BaiduWordContent, len(opts))
		//
		//	//getMagiCookie(*word+" "+(*curOptions)[0])
		//
		//	for _i, opt := range opts {
		//		wg.Add(1)
		//		magiContents[_i] = &BaiduWordContent{}
		//		go magi(&wg, *word+" "+opt, magiContents[_i])
		//	}
		//	wg.Wait()
		//	for _, c := range magiContents {
		//		if c != nil {
		//			baiduWordContent.Contents = append(baiduWordContent.Contents, (*c).Contents...)
		//		}
		//		//log.Println(_i,c)
		//	}
		//
		//	result, matches = highLight(baiduWordContent, &opts, curQuestion.OptionWords)
		//	sort.Sort(ContentSort(result))
		//	//fmt.Println(result)
		//	for _, r := range result {
		//		if r.Content != "" {
		//			contents = append(contents, r.Content)
		//		}
		//	}
		//}
	}

	go SendSearchContent(&contents, &matches, &curQuestion.Round)

}

func OptionsToStringArr() *[]string {
	opts := make([]string, len(curQuestion.Options))
	for _i, _o := range curQuestion.Options {
		opts[_i] = _o.Text
	}
	return &opts
}
func SetMagiCookie(cookie string) {
	magiCookie = cookie
}

func highLight(wordContent *BaiduWordContent, options *[]string, optionWords *map[string]bool) ([]ContentResult, map[string]*OptionMatch) {
	go log.LogPrint("开始着色")
	//分词排序
	var optionWordArr, titleWordArr []string
	if optionWords != nil {
		for w := range *optionWords {
			optionWordArr = append(optionWordArr, w)
		}
	}
	for w := range (*wordContent).Words {
		titleWordArr = append(titleWordArr, w)
	}
	if len(optionWordArr) == 0 {
		optionWordArr = append(optionWordArr, *options...)
	}

	sort.Sort(WordSort(*options))
	sort.Sort(WordSort(optionWordArr))
	sort.Sort(WordSort(titleWordArr))
	//正则
	optionReg, _ := regexp.Compile(strings.Join(*options, "|"))
	wordReg, _ := regexp.Compile(strings.Join(optionWordArr, "|"))
	titleReg, _ := regexp.Compile(strings.Join(titleWordArr, "|"))
	specialReg, _ := regexp.Compile("答案")
	//fmt.Println(strings.Join(*options, "|"))
	//fmt.Println(strings.Join(optionWordArr, "|"))
	//fmt.Println(wordContent.Contents)

	result := make([]ContentResult, len((*wordContent).Contents))
	matches := make(map[string]*OptionMatch)
	for _, w := range *options {
		matches[w] = &OptionMatch{}
	}
	for resIndex, content := range wordContent.Contents {
		//optionContentBit := make([]bool, len(content))
		//titleContentBit := make([]bool, len(content))
		resultBit := make([]byte, len(content))
		wordMatches := wordReg.FindAllStringIndex(content, -1)
		optionMatches := optionReg.FindAllStringIndex(content, -1)
		titleMatches := titleReg.FindAllStringIndex(content, -1)
		specialMatches := specialReg.FindAllStringIndex(content, -1)
		length := 0
		index := 0
		//选项
		for _, optionMatch := range optionMatches {
			length++
			if optionMatch[0] != index {
				length++
			}
			for i := optionMatch[0]; i < optionMatch[1]; i++ {
				resultBit[i] = 'A'
			}
			index = optionMatch[1]
			matches[content[optionMatch[0]:optionMatch[1]]].PMatch++
		}

		hasOption := false

		//分词
		index = 0
		for _, wordMatch := range wordMatches {
			hasOption = false
			for i := wordMatch[0]; i < wordMatch[1]; i++ {
				if resultBit[i] != byte(0) {
					hasOption = true
					break
				}
			}
			if !hasOption {
				length++
				if wordMatch[0] != index {
					length++
				}
				for i := wordMatch[0]; i < wordMatch[1]; i++ {
					resultBit[i] = 'W'
				}
				index = wordMatch[1]
			}
			str := content[wordMatch[0]:wordMatch[1]]
			for _, o := range *options {
				if strings.Contains(o, str) {
					matches[o].FMatch++
				}
			}
		}

		if length == 0 {
			continue
		}

		//答案
		index = 0
		for _, specialMatch := range specialMatches {
			hasOption = false
			for i := specialMatch[0]; i < specialMatch[1]; i++ {
				if resultBit[i] != byte(0) {
					hasOption = true
					break
				}
			}
			if !hasOption {
				length++
				if specialMatch[0] != index {
					length++
				}
				for i := specialMatch[0]; i < specialMatch[1]; i++ {
					resultBit[i] = 'S'
				}
				index = specialMatch[1]
			}
		}

		//题目
		index = 0
		titleMax := 0
		for _, titleMatch := range titleMatches {
			hasOption = false
			for i := titleMatch[0]; i < titleMatch[1]; i++ {
				if resultBit[i] != byte(0) {
					hasOption = true
					break
				}
			}
			if titleMatch[1]-titleMatch[0] > titleMax {
				titleMax = titleMatch[1] - titleMatch[0]
			}
			if !hasOption {
				length++
				if titleMatch[0] != index {
					length++
				}
				for i := titleMatch[0]; i < titleMatch[1]; i++ {
					resultBit[i] = '1'
				}
				index = titleMatch[1]
			}
		}

		res := make([]string, length)
		index = 0
		preByte := byte('0')
		emStart := byte('0')
		for i, b := range resultBit {
			if b != preByte {
				res = append(res, content[index:i])
				if emStart == 'A' {
					res = append(res, "</em>")
				} else if emStart == '1' {
					res = append(res, "</bm>")
				} else if emStart == 'S' {
					res = append(res, "</cm>")
				} else if emStart == 'W' {
					res = append(res, "</em>")
				}

				if b == 'A' {
					res = append(res, "<em>")
				} else if b == '1' {
					res = append(res, "<bm>")
				} else if b == 'S' {
					res = append(res, "<cm>")
				} else if b == 'W' {
					res = append(res, "<em>")
				} else {
					if preByte == 'A' {
						res = append(res, "</em>")
					} else if preByte == '1' {
						res = append(res, "</bm>")
					} else if preByte == 'S' {
						res = append(res, "</cm>")
					} else if preByte == 'W' {
						res = append(res, "</em>")
					}
				}
				emStart = b
				index = i
			}
			preByte = b
		}
		res = append(res, content[index:])
		if emStart == 'A' {
			res = append(res, "</em>")
		} else if emStart == '1' {
			res = append(res, "</bm>")
		} else if emStart == 'S' {
			res = append(res, "</cm>")
		} else if emStart == 'W' {
			res = append(res, "</em>")
		}

		//*result = append(*result, strings.Join(res, ""))
		result[resIndex] = ContentResult{
			Content:       strings.Join(res, ""),
			PMatch:        len(optionMatches),
			FMatch:        len(wordMatches),
			TitleMatch:    len(titleMatches),
			TitleMatchMax: titleMax,
		}
	}
	return result, matches
}
func NewAnswer(question *Question){
	curQuestion.Result = question.Result
	go SendNewAnswer(question)
}

func XiaoYuan() {
	_img := adb.CreateQuestionImage(&curQuestion.Title, OptionsToStringArr())
	adb.SaveImage(&questionImagePath, _img)
	//再拍一张
	adb.AdbShellInputTap(500, 1250)
	adb.TimeSleepDuration(100)
	//选择相册
	adb.AdbShellInputTap(45, 1240)
	adb.TimeSleepDuration(1300)
	//图片
	adb.AdbShellInputTap(200, 135)
	adb.TimeSleepDuration(100)
}

type Question struct {
	Round       int32            `json:"round"`
	Options     []Option         `json:"options"`
	Title       string           `json:"title"`
	OptionWords *map[string]bool `json:"optionWords"`
	Result      string           `json:"result"`
}

type Option struct {
	Text   string   `json:"text"`
	PMatch int      `json:"pMatch"`
	FMatch int      `json:"fMatch"`
	Words  []string `json:"words"`
}

type BaiduWordContent struct {
	Words    map[string]bool
	Contents []string
}

type BaiduWordResponse struct {
	Items     []BaiduWordItem `json:"items"`
	ErrorCode int             `json:"error_code"`
}

type BaiduWordItem struct {
	Item string `json:"item"`
	Pos  string `json:"pos"`
}

type BaiduTokenResponse struct {
	ErrorMessage string `json:"error_description"`
	Token        string `json:"access_token"`
}

type ContentResult struct {
	Content       string `json:"content"`
	PMatch        int    `json:"pMatch"`
	FMatch        int    `json:"fMatch"`
	TitleMatch    int
	TitleMatchMax int
}
type ContentSort []ContentResult

func (s ContentSort) Len() int      { return len(s) }
func (s ContentSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s ContentSort) Less(i, j int) bool {
	if s[i].PMatch > s[j].PMatch {
		return true
	} else if s[i].PMatch < s[j].PMatch {
		return false
	} else if s[i].PMatch == s[j].PMatch {
		if s[i].FMatch > s[j].FMatch {
			return true
		} else if s[i].FMatch < s[j].FMatch {
			return false
		} else if s[i].FMatch == s[j].FMatch {
			if s[i].TitleMatchMax > s[j].TitleMatchMax {
				return true
			} else if s[i].TitleMatchMax < s[j].TitleMatchMax {
				return false
			} else if s[i].TitleMatchMax == s[j].TitleMatchMax {
				if s[i].TitleMatch > s[j].TitleMatch {
					return true
				} else if s[i].TitleMatch <= s[j].TitleMatch {
					return false
				}
			}
		}
	}

	return false
}

type WordSort []string

func (s WordSort) Len() int           { return len(s) }
func (s WordSort) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s WordSort) Less(i, j int) bool { return len(s[i]) > len(s[j]) }
